﻿/* 
   Copyright 2011 LUCA Studios LLC & John Evans

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

namespace LUCAStudios.Silverlight.Controls
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using Common;

    /// <summary>
    /// Provides a common base class for LUCA Studios Controls
    /// </summary>
    public abstract class ControlBase : Control
    {
        private readonly Dictionary<DependencyProperty, object> _stateBag = new Dictionary<DependencyProperty, object>();
        private bool _loaded;

        /// <summary>
        /// Constructor
        /// </summary>
        protected ControlBase()
        {
            Loaded += ControlBaseLoaded;
            Unloaded += ControlBaseUnloaded;
        }

        /// <summary>
        /// Fires when the LiquidControl is selected (clicked) by the user.
        /// </summary>
        public event EventHandler Selected;

        /// <summary>
        /// Gets the LUCALayoutPanel root of this control.  Will return null if the control's Loaded event has not yet fired.
        /// </summary>
        public LUCALayoutPanel LayoutContext { get; internal set; }

        /// <summary>
        /// The ContainerBase control that this control currently resides within
        /// </summary>
        public ContainerBase ContainerContext { get; internal set; }

        /// <summary>
        /// Provides preservation of previous container context for restores
        /// </summary>
        public PreviousContainerContextInfo PreviousContainerContext { get; internal set; }

        private void ControlBaseLoaded(object sender, EventArgs e)
        {
            ApplyTemplate();

            //only get the parent layout on the first load
            if (!_loaded)
            {
                _loaded = true;

                LayoutContext = this.FindParentByType<LUCALayoutPanel>();
                if (Peristent) LUCALayoutPanel.PersistentControlRegistry.Add(this);
            }

            //parent container refreshed after each load
            ContainerContext = this.FindParentByType<ContainerBase>();

            OnLoaded();
        }

        void ControlBaseUnloaded(object sender, RoutedEventArgs e)
        {
            OnUnLoaded();
        }

        /// <summary>
        /// Called when the LUCA control is loaded
        /// </summary>
        protected virtual void OnLoaded(){}

        /// <summary>
        /// Called when the LUCA control is unloaded
        /// </summary>
        protected virtual void OnUnLoaded(){}

        /// <summary>
        /// Called when the LUCA control is selected
        /// </summary>
        protected virtual void OnSelected()
        {
            Focus();
            if (Selected == null) return;
            Selected(this, new EventArgs());
        }

        /// <summary>
        /// Backing property of for the Peristent Dependency Property
        /// </summary>
        public static readonly DependencyProperty PeristentProperty =
            DependencyProperty.Register("Peristent", typeof (bool), typeof (ControlBase), new PropertyMetadata(false));

        /// <summary>
        /// Set is true to auto-register to control to the LUCALayoutPanel.PersistentControlRegistry
        /// This is typically used for controls like windows where the developer needs to hold a 
        /// global reference to the control throughout the application lifetime.
        /// This is a Dependency Property
        /// </summary>
        public bool Peristent
        {
            get { return (bool) GetValue(PeristentProperty); }
            set { SetValue(PeristentProperty, value); }
        }


        /// <summary>
        /// Stores a dependency property setting in the StateBag collection.
        /// </summary>
        /// <param name="property">The dependency property to store.</param>
        /// <param name="value">The value of the depency property to store.</param>
        /// <param name="overwrite">True will overwrite existing dependency property value.  False will not overwrite existing, 
        /// but will create a new entry.</param>
        internal virtual void StoreProperty(DependencyProperty property, object value, bool overwrite)
        {
            if (_stateBag.ContainsKey(property) && !overwrite) return;

            if (_stateBag.ContainsKey(property))
            {
                _stateBag[property] = value;
            }
            else
            {
                _stateBag.Add(property, value);
            }
        }

        /// <summary>
        /// Retrieves the value of the provided dependency property from the StateBag.  Returns null if property does not exist.
        /// </summary>
        /// <param name="property">Name of the dependency property to retrieve the value for.</param>
        /// <returns></returns>
        internal virtual object RetrieveProperty(DependencyProperty property)
        {
            return _stateBag.ContainsKey(property)
                ? _stateBag[property]
                : default(object);
        }

        /// <summary>
        /// Retrieves the value of the provided dependency property from the StateBag.  Returns null if property does not exist.
        /// </summary>
        /// <param name="property">Name of the dependency property to retrieve the value for.</param>
        /// <returns></returns>
        internal virtual T RetrieveProperty<T>(DependencyProperty property)
        {
            return _stateBag.ContainsKey(property)
                ? (T)_stateBag[property]
                : default(T);
        }

        /// <summary>
        /// Assigns the currently stored dependency property value to the control.
        /// </summary>
        /// <param name="property"></param>
        internal virtual void AssignProperty(DependencyProperty property)
        {
            if (!_stateBag.ContainsKey(property)) return;
            if (property.Equals(Canvas.LeftProperty))
            {
                if (LayoutContext == null) return;

                var rv = LayoutContext.RootVisual;
                var pos = this.GetPosition(rv);
                this.AbsoluteTransformPositionTo(new Point((double)_stateBag[property], pos.Y), rv);
            }
            else if (property.Equals(Canvas.TopProperty))
            {
                if (LayoutContext == null) return;

                var rv = LayoutContext.RootVisual;
                var pos = this.GetPosition(rv);
                this.AbsoluteTransformPositionTo(new Point(pos.X, (double)_stateBag[property]), rv);
            }
            else
            {
                SetValue(property, _stateBag[property]);
            }
        }

        /// <summary>
        /// If exists, clears the dependency property from the StateBag
        /// </summary>
        /// <param name="property"></param>
        internal virtual void ClearProperty(DependencyProperty property)
        {
            if (_stateBag.ContainsKey(property))
                _stateBag.Remove(property);
        }

        /// <summary>
        /// Assigns all properties currently in the StateBag collection to the control, then clears them from the collection.
        /// </summary>
        internal virtual void AssignAllAndClear()
        {
            while (_stateBag.Any())
            {
                AssignProperty(_stateBag.First().Key);
                _stateBag.Remove(_stateBag.First().Key);
            }
        }

        /// <summary>
        /// Removes all items from the StateBag collection
        /// </summary>
        internal virtual void ClearAll()
        {
            _stateBag.Clear();
        }
    }
}